export class Container {
    static defineProperty<T>(type: ServiceType<T>): T {
        return new ServiceProxy(type) as IService as T;
    }
    constructor() {
    }
    Get<T>(type: ServiceType<T>): T { throw "Not implemented" }
    Set<T>(type: ServiceType<T>, build?: () => T): void { throw "Not implemented" }
    GetInstances(): ServiceDescriptor[] { throw "Not implemented" }
    protected initDefineProperty(t: any) {
        for (let key in t) {
            if (t[key] instanceof ServiceProxy) {
                t[key] = this.Get((<ServiceProxy>t[key]).type);
            }
        }
    }
}
export interface ServiceType<T extends IService> { new(): T };
export interface IService {
    Init?: () => void;
    Uninit?: () => void;
}
export interface ServiceDescriptor {
    //typeName: string,
    Type: ServiceType<IService>
    Value?: IService,
    Build?: () => IService,
}
class ServiceProxy {
    constructor(public type: ServiceType<IService>) {
    }
}
export class ContainerHost {
    private static _Current: Container;
    static get Current() {
        if (!this._Current)
            this._Current = new _Container();
        return this._Current;
    }
}

class _Container extends Container {
    private TypeInfos: Map<ServiceType<IService>, ServiceDescriptor> = new Map();
    constructor() {
        super();
        this.Set(Container, () => this);
    }
    GetInstances(): ServiceDescriptor[] {
        return Array.from(this.TypeInfos.values());
    }
    Get<T>(type: ServiceType<T>): T {
        if (!this.TypeInfos.has(type)) {
            this.Set(type);
        }
        let typeInfo = this.TypeInfos.get(type) as ServiceDescriptor;
        if (!typeInfo.Value) {
            if (typeInfo.Build)
                typeInfo.Value = typeInfo.Build();
            else
                typeInfo.Value = new typeInfo.Type();

            this.initDefineProperty(typeInfo.Value);

            if (typeInfo.Value!.Init)
                typeInfo.Value!.Init();
        }
        return typeInfo.Value as T;
    }
    Set<T>(type: ServiceType<T>, build?: () => T) {
        this.TypeInfos.set(type, {
            //typeName: type.name, 
            Type: type, Value: undefined, Build: build
        });
    }
    Run(...types: ServiceType<IService>[]) {
        for (let i = 0; i < types.length; i++)
            this.Get(types[i]);
    }
}